//
//  application.h
//
#pragma once
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <Vulkan\vulkan.h>
#include "Vector.h"
#include "Scene.h"
/*
====================================================
Application
====================================================
*/
struct SwapchainSupportDetails
{
	VkSurfaceCapabilitiesKHR capabilities = {};
	std::vector<VkSurfaceFormatKHR> formats;
	std::vector<VkPresentModeKHR> presentModes;
};
class Application {
public:
	Application() : m_isPaused( false ), m_stepFrame( true ) {}
	~Application();

	void Initialize();
	void Run();

private:
	void InitializeGLFW();
	bool InitializeVulkan();
	void Cleanup();
	void UpdateUniforms();
	void BeginFrame(uint32_t);
	void DrawFrame();
	void EndFrame();
	void BeginRenderPass();
	void EndRenderPass();
	void ResizeWindow( int windowWidth, int windowHeight );
	void MouseMoved( float x, float y );
	void MouseScrolled( float z );
	void Keyboard( int key, int scancode, int action, int modifiers );

	static void OnWindowResized( GLFWwindow * window, int width, int height );
	static void OnMouseMoved( GLFWwindow * window, double x, double y );
	static void OnMouseWheelScrolled( GLFWwindow * window, double x, double y );
	static void OnKeyboard( GLFWwindow * window, int key, int scancode, int action, int modifiers );
private:
	std::vector<const char *> GetGLFWRequiredExtensions() const;
	void CreateInstance();
	VkResult CreateDevice();

	bool CheckValidationLayerSupport(const std::vector<const char*>& validationLayers);
	bool SetupDebugCallbacks(VkInstance instance, VkDebugUtilsMessengerEXT* messenger, VkDebugReportCallbackEXT* reportCallback);
	VkResult FindSuitablePhysicalDevice();
	uint32_t FindQueueFamilies(VkQueueFlags desiredFlags);
	VkFormat FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
	uint32_t FindMemoryType( uint32_t typeFilter, VkMemoryPropertyFlags properties);
	VkResult CreateSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t graphicsFamily, uint32_t width, uint32_t height, VkSwapchainKHR* swapchain, bool supportScreenshots = false);
	size_t CreateSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, std::vector<VkImage>& swapchainImages, std::vector<VkImageView>& swapchainImageViews);
	bool CreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VkImageView* imageView, VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D, uint32_t layerCount = 1, uint32_t mipLevels = 1);
	SwapchainSupportDetails QuerySwapchainSupport(VkPhysicalDevice device, VkSurfaceKHR surface);
	VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
	VkPresentModeKHR ChooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
	uint32_t ChooseSwapImageCount(const VkSurfaceCapabilitiesKHR& capabilities);
	VkResult CreateSemaphores(VkDevice device, VkSemaphore* outSemaphore);
	VkResult CreateGraphicsPipeline();
	void CreateRenderPass();
	void CreateFrameBuffer();
	void CreateSyncObjects();
	VkShaderModule CreateShaderModule(const std::vector<char>& code);
private:
	GLFWwindow* m_Window{nullptr};
	VkInstance m_Instance{ VK_NULL_HANDLE };
	VkSurfaceKHR m_Surface{VK_NULL_HANDLE};
	VkPhysicalDevice m_PhysicalDevice{ VK_NULL_HANDLE };
	VkDevice m_Device{ VK_NULL_HANDLE };
	VkQueue m_GraphicsQueue{VK_NULL_HANDLE};
	VkQueue m_ComputeQueue{VK_NULL_HANDLE};

	VkSwapchainKHR m_Swapchain{ VK_NULL_HANDLE };
	VkSemaphore m_Semaphore{VK_NULL_HANDLE};
	VkSemaphore m_RenderSemaphore{VK_NULL_HANDLE};
	VkFence m_FlightFence{ VK_NULL_HANDLE };
	std::vector<VkImage> m_SwapchainImages;
	std::vector<VkImageView> m_SwapchainImageViews;
	uint32_t graphicsFamily;
	uint32_t computeFamily;

	std::vector<VkFramebuffer> m_Framebuffers;
	VkRenderPass m_RenderPass{ VK_NULL_HANDLE };
	VkPipelineLayout m_PipelineLayout{ VK_NULL_HANDLE };
	VkPipeline m_GraphicsPipeline{ VK_NULL_HANDLE };
	VkShaderModule vsShaderModule;
	VkShaderModule fsShaderModule;
	VkFormat m_SwapchainImageFormat;
	VkExtent2D m_SwapchainExtent;

	VkCommandPool m_CommandPool{ VK_NULL_HANDLE };
	std::vector<VkCommandBuffer> m_CommandBuffers;
	VkDebugUtilsMessengerEXT m_DubugMessenger{ VK_NULL_HANDLE };
	bool useCompute{false};	

	// User input
	Vec2 m_mousePosition;
	Vec3 m_cameraFocusPoint;
	float m_cameraPositionTheta;
	float m_cameraPositionPhi;
	float m_cameraRadius;
	bool m_isPaused;
	bool m_stepFrame;
	bool m_IsResizeWindow = false;
	Scene* m_scene;
	static const int WINDOW_WIDTH = 960;
	static const int WINDOW_HEIGHT = 720;
	static const bool m_enableLayers = true;
	static const int MAX_FRAMES_IN_FLIGHT = 2;
};